#!/usr/bin/env python
"""--------------------------------------------------------------------------------------------------
Filename:   plotpanel.py
Author:     Ben Han (uha10@uclive.ac.nz)
Date:       17 May 2010
Description:
    Module containing GUI elements from which the main plot window is composed.
    
Previous authors of CLASS PlotPanel: Edward Abraham, John Bender, CWRU, 10 Sep 08
Modified by:    Ben Han (27 April 2010)

Original comments----------------------------------------------------------
New version based on the original by Edward Abraham, incorporating some
forum discussion, minor bugfixes, and working for Python 2.5.2,
wxPython 2.8.7.1, and Matplotlib 0.98.3.
I haven't found an advantage to using the NoRepaintCanvas, so it's removed.
John Bender, CWRU, 10 Sep 08
---------------------------------------------------------------------------
--------------------------------------------------------------------------------------------------"""

import wx

#===============================================================================
# CLASS: MyPlotPanel( PlotPanel(wx.Panel) )
#    Main plotting class. Uses PlotPanel class (derived from wx.Panel)
#===============================================================================
class PlotPanel(wx.Panel):
    """ Live updating plot for feedback.
        self.target - target used in the plot, either 2D line object or Rectangle object
    """
    def __init__( self, parent, color=None, dpi=80, **kwargs ):
        from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg
        from matplotlib.figure import Figure
        # initialize Panel
        if 'id' not in kwargs.keys():
            kwargs['id'] = wx.ID_ANY
        if 'style' not in kwargs.keys():
            kwargs['style'] = wx.NO_FULL_REPAINT_ON_RESIZE
        wx.Panel.__init__( self, parent, **kwargs )
        self.SetSize((800,600))

        # initialize matplotlib stuff
        self.figure = Figure( None, dpi )
        self.canvas = FigureCanvasWxAgg( self, -1, self.figure )
        self.SetColor( color )

        self._resizeflag = False

        self.Bind(wx.EVT_IDLE, self._onIdle)
        self.Bind(wx.EVT_SIZE, self._onSize)
        
        self.parent = parent
        
    def SetColor( self, rgbtuple=None ):
        """Set figure and canvas colours to be the same."""
        if rgbtuple is None:
            rgbtuple = wx.SystemSettings.GetColour( wx.SYS_COLOUR_BTNFACE ).Get()
        clr = [c/255. for c in rgbtuple]
        self.figure.set_facecolor( clr )
        self.figure.set_edgecolor( clr )
        self.canvas.SetBackgroundColour( wx.Colour( *rgbtuple ) )

    def _onSize( self, event ):
        self._resizeflag = True

    def _onIdle( self, evt ):
        if self._resizeflag:
            self._resizeflag = False
            self._SetSize()

    def _SetSize( self ):
        pixels = self.parent.GetClientSizeTuple()
        button_panel_size = self.parent.GetParent().plotctrl.minsize
        w = pixels[0]
        h = pixels[1] - button_panel_size[1]
        pixels = (w, h)
        self.SetSize( pixels )
        self.canvas.SetSize( pixels )
        self.figure.set_size_inches( float( pixels[0] )/self.figure.get_dpi(),
                                     float( pixels[1] )/self.figure.get_dpi() )

        
#===============================================================================
# CLASS: PlotControl(wx.Panel)
#    Container class for the control buttons in the plot window
#===============================================================================
class PlotControl(wx.Panel):
    def __init__(self, parent, id, app):
        wx.Panel.__init__(self, parent, id)
        self.app = app

        #Create Sizers

        self.sizer = wx.BoxSizer(wx.HORIZONTAL)
        self.box1 = wx.StaticBox(self, -1, "Control")
        self.sizer1 = wx.StaticBoxSizer(self.box1, wx.HORIZONTAL)
        # Add checkboxes to sizer
        self.box2 = wx.StaticBox(self, -1, "Plot options")
        self.sizer2 = wx.StaticBoxSizer(self.box2, wx.VERTICAL)
        self.sizer3 = wx.GridSizer(2,2,0,0)
        self.box4 = wx.StaticBox(self, -1, "Trial status")
        self.sizer4 = wx.StaticBoxSizer(self.box4, wx.HORIZONTAL)
        self.box5 = wx.StaticBox(self, -1, "Target range limits")
        self.sizer5 = wx.StaticBoxSizer(self.box5, wx.VERTICAL)
        self.sizer6 = wx.FlexGridSizer(2,2)
        
        # Create buttons
        self.next_button = wx.Button(self, -1, "Start", )
        self.recalib_button = wx.Button(self, -1, "Re-calibrate")
        self.reset_button = wx.Button(self, -1, "Reset target")
        self.pause_button = wx.Button(self, -1, "Pause")
        self.save_button = wx.Button(self, -1, "Save and exit")
        self.menu_button = wx.Button(self, -1, "Exit to menu")

        # Create check boxes
        self.cb_grid = wx.CheckBox(self, -1, "Grid")
        self.cb_plot = wx.CheckBox(self, -1, "Plot")
        self.cb_time = wx.CheckBox(self, -1, "Time")
        self.cb_ampl = wx.CheckBox(self, -1, "Amplitude")
        
        # Create status text control
        self.status_text = wx.TextCtrl(self, -1, style=wx.TE_READONLY)
        
        # Oshi's buttons----------------------------------
        self.upper_limit_spin = wx.SpinCtrl(self)
        self.upper_limit_spin.SetSize((50,30))
        self.upper_limit_spin.SetRange(0, 100)
        self.upper_limit_spin.SetValue(self.app.cfg._upper_limit*100)
        self.Bind(wx.EVT_SPINCTRL, self.on_upper_spin, self.upper_limit_spin)
        self.upper_limit_spin_text = wx.StaticText(self, -1, 'Upper', size=(50,30))
        
        self.lower_limit_spin = wx.SpinCtrl(self)
        self.lower_limit_spin.SetSize((50,30))
        self.lower_limit_spin.SetRange(0, 100)
        self.lower_limit_spin.SetValue(self.app.cfg._lower_limit*100)
        self.Bind(wx.EVT_SPINCTRL, self.on_lower_spin, self.lower_limit_spin)
        self.lower_limit_spin_text = wx.StaticText(self, -1, 'Lower', size=(50,30))
        # END Oshi's buttons------------------------------

        self.__set_properties()
        self.__do_layout()
        
        # Sets handlers
        self.Bind(wx.EVT_BUTTON, self.app.phdls.next_button, self.next_button)
        self.Bind(wx.EVT_BUTTON, self.app.phdls.recalibrate, self.recalib_button)
        self.Bind(wx.EVT_BUTTON, self.app.phdls.target_reset, self.reset_button)
        self.Bind(wx.EVT_BUTTON, self.app.phdls.pause, self.pause_button)
        self.Bind(wx.EVT_BUTTON, self.app.phdls.save_exit, self.save_button)
        self.Bind(wx.EVT_BUTTON, self.app.phdls.exit_plot, self.menu_button)

        self.Bind(wx.EVT_CHECKBOX, self.on_cb_grid, self.cb_grid)
        self.Bind(wx.EVT_CHECKBOX, self.on_cb_plot, self.cb_plot)
        self.Bind(wx.EVT_CHECKBOX, self.on_cb_time, self.cb_time)
        self.Bind(wx.EVT_CHECKBOX, self.on_cb_ampl, self.cb_ampl)
        
    def __set_properties(self):
        self.next_button.Enable(False)
        self.next_button.SetMinSize((70, 30))
        self.recalib_button.Disable()
        self.recalib_button.SetMinSize((70, 30))
        self.pause_button.Enable(False)
        self.pause_button.SetMinSize((70, 30))
        self.reset_button.Enable(False)
        self.reset_button.SetMinSize((80, 30))
        self.save_button.Enable(False)
        self.save_button.SetMinSize((100, 30))
        self.menu_button.SetMinSize((100, 30))
        self.status_text.SetMinSize((180, 40))
        self.status_text.SetFont(wx.Font(10, wx.DEFAULT, wx.NORMAL, wx.BOLD))
        cfg = self.app.cfg
        self.cb_grid.SetValue(cfg.default_grid)
        self.cb_plot.SetValue(cfg.default_plot)
        self.cb_time.SetValue(cfg.default_time)
        self.cb_ampl.SetValue(cfg.default_ampl)
        
    def __do_layout(self):
       
        self.sizer1.Add(self.next_button, 0, wx.ALL | wx.CENTRE, 5)
        self.sizer1.Add(self.recalib_button, 0, wx.ALL | wx.CENTRE, 5)
        self.sizer1.Add(self.pause_button, 0, wx.ALL | wx.CENTRE, 5)
        self.sizer1.Add(self.reset_button, 0, wx.ALL | wx.CENTRE, 5)
        self.sizer1.Add(self.save_button, 0, wx.ALL | wx.CENTRE, 5)
        self.sizer1.Add(self.menu_button, 0, wx.ALL | wx.CENTRE, 5)
        
        self.sizer3.Add(self.cb_grid, 0, wx.ALL | wx.CENTRE, 3)
        self.sizer3.Add(self.cb_plot, 0, wx.ALL | wx.CENTRE, 3)
        self.sizer3.Add(self.cb_time, 0, wx.ALL | wx.CENTRE, 3)
        self.sizer3.Add(self.cb_ampl, 0, wx.ALL | wx.CENTRE, 3)
        self.sizer2.Add(self.sizer3)
        # Add text control (status) to sizer
       
        self.sizer4.Add(self.status_text, 0, wx.EXPAND)
        
        # Oshi's spinners
        
        self.sizer6.Add(self.upper_limit_spin_text, 0, wx.ALL, 3)
        self.sizer6.Add(self.upper_limit_spin, 0, wx.ALL, 3)
        self.sizer6.Add(self.lower_limit_spin_text, 0, wx.ALL, 3)
        self.sizer6.Add(self.lower_limit_spin, 0, wx.ALL, 3)
        self.sizer5.Add(self.sizer6)
        # END Oshi's buttons
                
        # Add sizers to main sizer
        self.sizer.Add(self.sizer1, 0, wx.ALL | wx.EXPAND, 5)
        self.sizer.Add(self.sizer2, 0, wx.ALL | wx.EXPAND, 5)
        self.sizer.Add(self.sizer4, 0, wx.ALL | wx.EXPAND, 5)
        self.sizer.Add(self.sizer5, 0, wx.ALL | wx.EXPAND, 5)
        
        ms = self.sizer.GetMinSize()             # to be used by PlotPanel._SetSize()
        self.minsize = (ms[0], ms[1]+10)    # change this implementation to a general form to take into account borders
        self.SetSizer(self.sizer)
        
    def on_upper_spin(self, event):
        cfg = self.app.cfg
        cfg._upper_limit = self.upper_limit_spin.GetValue()/100.0
        range_max = [cfg._trial_len - cfg._x_margin, cfg._upper_limit*self.app.sess_status.baseline]
        self.app.plot.tfactory.set_range_max(range_max)
        
    def on_lower_spin(self, event):
        cfg = self.app.cfg
        cfg._lower_limit = self.lower_limit_spin.GetValue()/100.0
        range_min = [cfg._x_margin, cfg._lower_limit*self.app.sess_status.baseline]
        self.app.plot.tfactory.set_range_min(range_min)
        
    def on_cb_grid(self, event):
        self.app.ctrl.chkbx_ctrl.cb_grid.SetValue(self.cb_grid.GetValue())
        self.app.plot.draw_grid(self.cb_grid.GetValue())

    def on_cb_plot(self, event):
        self.app.plot.show_plot(self.cb_plot.IsChecked())

    def on_cb_time(self, event):
        self.app.ctrl.chkbx_ctrl.cb_time.SetValue(self.cb_time.GetValue())

    def on_cb_ampl(self, event):
        self.app.ctrl.chkbx_ctrl.cb_ampl.SetValue(self.cb_ampl.GetValue())
        
    def set_status(self, s_text):
        self.status_text.SetValue(s_text)

# End of plotpanel.py
